/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import UIKit

public enum SafeAreaType: UInt {
    // 状态栏
    case statusBar
    // 刘海
    case notch
    // 手势
    case gestureArea
    // 键盘
    case keyboard
    // 底部导航栏
    case navigationToolbar
}

public final class AWSafeAreaMonitor: NSObject {
    public static let shared = AWSafeAreaMonitor()
    
    private var updateHandler: ((String) -> Void)?
    private weak var targetWindow: UIWindow?
    private var isKeyboardVisible = false
    private var keyboardFrame = CGRect.zero
    private var hasAddedSafeAreaObserver = false
    private var hasKeyboardObserver = false
    private var lastKnownOrientation: UIInterfaceOrientation = .portrait
    private var lastInsets = UIEdgeInsets.zero
    private let observerViewTag = 999_999
    private var updateSafeAreaTask: DispatchWorkItem?
    
    public func addAvoidAreaListener(_ handler: @escaping (String) -> Void) {
        updateHandler = handler
        targetWindow = currentKeyWindow()
        lastKnownOrientation = currentInterfaceOrientation()
        
        setupNotifications()
        setupKeyboardObservers()
        addSafeAreaObserver()
    }
    
    public func removeAvoidAreaListener() {
        NotificationCenter.default.removeObserver(self)
        removeSafeAreaObserver()
        updateHandler = nil
    }

    private func setupNotifications() {
        NotificationCenter.default.removeObserver(self)
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleDeviceOrientationChange),
            name: UIDevice.orientationDidChangeNotification,
            object: nil
        )
    }
    
    private func setupKeyboardObservers() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardFrameWillChange(_:)),
            name: UIResponder.keyboardWillChangeFrameNotification,
            object: nil
        )
    }
    
    public func getWindowAvoidArea(for type: SafeAreaType) -> String {
        self.targetWindow = currentKeyWindow()
        let rect = calculateRect(for: type)
        let result = buildAvoidAreaResultAuto(rect: rect, areaType: type)
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: result, options: [.prettyPrinted])
            return String(data: jsonData, encoding: .utf8) ?? ""
        } catch {
            print("❌ JSON 构建失败: \(error)")
            return ""
        }
    }
    
    private func calculateRect(for type: SafeAreaType) -> CGRect {
        switch type {
        case .statusBar:
            return calculateStatusBarRect()
        case .notch:
            return calculateNotchAreaRect()
        case .gestureArea:
            return calculateGestureAreaRect()
        case .keyboard:
            return isKeyboardVisible ? keyboardFrame : .zero
        case .navigationToolbar:
            return calculateNavigationToolbarFrame()
        }
    }

    @objc private func keyboardFrameWillChange(_ notification: Notification) {
        guard let userInfo = notification.userInfo else { return }
        
        let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0
        let curve = UIView.AnimationOptions(rawValue: (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? 0) << 16)
        let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect) ?? .zero
        
        let keyboardFrameInView = targetWindow?.convert(endFrame, from: nil) ?? .zero
        isKeyboardVisible = endFrame.height > 0 && keyboardFrameInView.maxY <= UIScreen.main.bounds.height
        keyboardFrame = isKeyboardVisible ? keyboardFrameInView : .zero
        
        UIView.animate(withDuration: duration, delay: 0, options: curve) {
            self.calculateCurrentSafeAreas()
        }
    }
    
    private func addSafeAreaObserver() {
        guard !hasAddedSafeAreaObserver, let window = targetWindow else { return }
        removeSafeAreaObserver()
        let observerView = UIView(frame: window.bounds)
        observerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        observerView.tag = observerViewTag
        observerView.isHidden = true
        window.addSubview(observerView)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            guard let self = self else { return }
            
            // 添加 KVO 监听（此时布局已稳定）
            observerView.addObserver(
                self,
                forKeyPath: "safeAreaInsets",
                options: [.new],
                context: nil
            )
            
            self.hasAddedSafeAreaObserver = true
        }
    }

    private func removeSafeAreaObserver() {
        guard hasAddedSafeAreaObserver, let window = targetWindow else { return }
        
        if let observerView = window.viewWithTag(observerViewTag) {
            observerView.removeObserver(self, forKeyPath: "safeAreaInsets")
            observerView.removeFromSuperview()
        }
        hasAddedSafeAreaObserver = false
    }
    
    // MARK: - KVO
    override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "safeAreaInsets" {
            guard keyPath == "safeAreaInsets" else {
                    super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
                    return
                }
                
            if let newInsets = (change?[.newKey] as? NSValue)?.uiEdgeInsetsValue {
                   // 新增防御性检查：避免无意义的重复计算
                   guard newInsets != lastInsets else {
                       print("⏭️ 安全区域未变化，跳过更新")
                       return
                   }
                   
                   lastInsets = newInsets
                   
                   // 取消前一次未执行的延迟任务（避免重复计算）
                   updateSafeAreaTask?.cancel()
                   
                   // 创建新的延迟任务（0.05秒足够覆盖高频触发场景）
                   let task = DispatchWorkItem { [weak self] in
                       guard let self = self else { return }
                       DispatchQueue.main.async { [weak self] in
                           self?.calculateCurrentSafeAreas()
                       }
                   }
                   
                updateSafeAreaTask = task
                   DispatchQueue.main.asyncAfter(deadline: .now() + 0.05, execute: task)
               }
        }
    }
        
        

    @objc private func handleDeviceOrientationChange() {
        triggerSafeAreaUpdateIfNeeded()
    }

    @objc private func handleSafeAreaChange() {
        triggerSafeAreaUpdateIfNeeded()
    }
    
    private func triggerSafeAreaUpdateIfNeeded() {
        let currentOrientation = currentInterfaceOrientation()
        guard currentOrientation != lastKnownOrientation else { return }
       lastKnownOrientation = currentOrientation
        updateSafeAreaTask?.cancel()
        // 创建新的延迟任务（0.2秒足够覆盖高频触发场景）
        let task = DispatchWorkItem { [weak self] in
            guard let self = self else { return }
            
            // 确保在主线程执行UI操作（虽然asyncAfter默认主队列，但双重保险）
            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                
                // 更新目标窗口（使用弱引用避免循环引用）
                self.targetWindow = self.currentKeyWindow()
                
                // 强制窗口刷新布局（触发子视图布局更新）
                self.targetWindow?.layoutIfNeeded()
                
                // 计算当前安全区域（核心业务逻辑）
                self.calculateCurrentSafeAreas()
                
                print("✅ 安全区域更新执行（仅最后一次触发）")
            }
        }
        
        // 保存任务并延迟执行
        updateSafeAreaTask = task
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: task)
                
    }
    
    private func currentKeyWindow() -> UIWindow? {
        if #available(iOS 15.0, *) {
            return UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .flatMap { $0.windows }
                .first { $0.isKeyWindow }
        } else if #available(iOS 13.0, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }

    private func currentInterfaceOrientation() -> UIInterfaceOrientation {
        if #available(iOS 13.0, *) {
            // 优先获取当前前台活动的窗口场景（兼容多窗口模式）
            let activeScene = UIApplication.shared.connectedScenes
                .filter { $0.activationState == .foregroundActive }
                .compactMap { $0 as? UIWindowScene }
                .first
            
            // 从场景中获取界面方向（场景存在时）
            if let scene = activeScene {
                return scene.interfaceOrientation
            }
            
            // 场景不存在时，回退到主窗口的方向（兼容旧逻辑）
            return UIApplication.shared.windows
                .filter { $0.isKeyWindow }
                .first?.windowScene?.interfaceOrientation ?? .portrait
        } else {
            // iOS 13 以下使用状态栏方向（已弃用但兼容）
            return UIApplication.shared.statusBarOrientation
        }
    }
    
    private func transformedRectForOrientation(_ originalRect: CGRect) -> CGRect {
        let orientation: UIInterfaceOrientation
        if #available(iOS 13.0, *) {
            orientation = currentKeyWindow()?.windowScene?.interfaceOrientation ?? .unknown
        } else {
            orientation = UIApplication.shared.statusBarOrientation
        }

        if orientation.isLandscape {
            return CGRect(
                x: originalRect.origin.y,
                y: originalRect.origin.x,
                width: originalRect.height,
                height: originalRect.width
            )
        }

        return originalRect
    }


   private func calculateStatusBarRect() -> CGRect {
        guard let window = currentKeyWindow() else {
            return .zero
        }

        let statusBarHeight = window.safeAreaInsets.top
        let statusBarWidth = window.bounds.width

        var isStatusBarHidden = false

        if #available(iOS 13.0, *) {
            if let scene = window.windowScene,
               let statusBarManager = scene.statusBarManager {
                isStatusBarHidden = statusBarManager.isStatusBarHidden
            }
        } else {
            isStatusBarHidden = UIApplication.shared.isStatusBarHidden
        }

        if isStatusBarHidden || statusBarHeight <= 0 || statusBarWidth <= 0 {
            return .zero
        }

        return CGRect(x: 0, y: 0, width: statusBarWidth, height: statusBarHeight)
    }

    
   private func calculateNotchAreaRect() -> CGRect {
        guard let window = currentKeyWindow() else {
            return .zero
        }

        let insets = window.safeAreaInsets
        let bounds = window.bounds

        let orientation: UIInterfaceOrientation
        if #available(iOS 13.0, *) {
            orientation = window.windowScene?.interfaceOrientation ?? .unknown
        } else {
            orientation = UIApplication.shared.statusBarOrientation
        }

        let isiPhone = UIDevice.current.userInterfaceIdiom == .phone
        let hasNotch = isiPhone && (insets.top >= 44 || insets.left > 0 || insets.right > 0)

        if !hasNotch {
            return .zero
        }

        if orientation.isPortrait {
            return CGRect(x: 0, y: 0, width: bounds.width, height: insets.top)
        }

        if orientation.isLandscape {
            let isPhysicalRight = orientation == .landscapeLeft

            if insets.left >= 44 {
                if isPhysicalRight {
                    // 刘海在右侧
                    return CGRect(x: bounds.width - insets.right, y: 0, width: insets.right, height: bounds.height)
                } else {
                    // 刘海在左侧
                    return CGRect(x: 0, y: 0, width: insets.left, height: bounds.height)
                }
            }
        }

        return .zero
    }
    
    private func calculateGestureAreaRect() -> CGRect {
        return .zero
    }
    
    private func calculateNavigationToolbarFrame() -> CGRect {
        guard let window = currentKeyWindow() else {
               return .zero
           }

           let insets = window.safeAreaInsets
           guard insets.bottom > 0 else {
               return .zero
           }

           let bounds = transformedRectForOrientation(window.bounds)

           return CGRect(
               x: 0,
               y: bounds.height - insets.bottom,
               width: bounds.width,
               height: insets.bottom
           )
    }
    
    @objc private func calculateCurrentSafeAreas() {
        var areas: [SafeAreaType: CGRect] = [:]

        // 状态栏
        let statusBarFrame = calculateStatusBarRect()
        areas[.statusBar] = statusBarFrame

        // 刘海区域
        let notchRect = calculateNotchAreaRect()
        areas[.notch] = notchRect

        // 手势区域
        let gestureRect = calculateGestureAreaRect()
        areas[.gestureArea] = gestureRect

        // 键盘区域
        if isKeyboardVisible {
            areas[.keyboard] = keyboardFrame
        }

        // 底部导航栏
        let toolbarRect = calculateNavigationToolbarFrame()
        areas[.navigationToolbar] = toolbarRect

        // 封装数据，逐项回调
        for (key, value) in areas {
            let areaDict: [String: Any] = [
                "type": key.rawValue,
                "area": buildAvoidAreaResultAuto(rect: value, areaType: key)
            ]

            do {
                let jsonData = try JSONSerialization.data(withJSONObject: areaDict, options: [])
                if let jsonString = String(data: jsonData, encoding: .utf8) {
                    updateHandler?(jsonString)
                }
            } catch {
                print("❌ JSON 构建失败: \(error)")
            }
        }
    }

    
    private func sendResult(_ areas: [String: Any]) {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: areas, options: [])
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                DispatchQueue.main.async {
                    self.updateHandler?(jsonString)
                }
            }
        } catch {
            print("JSON serialization error: \(error)")
        }
    }
    
    
    func buildAvoidAreaResult(visible: Bool,
                               leftRect: CGRect,
                               topRect: CGRect,
                               rightRect: CGRect,
                               bottomRect: CGRect) -> [String: Any] {
        return [
            "visible": visible,
            "leftRect": rectToDictionary(leftRect),
            "topRect": rectToDictionary(topRect),
            "rightRect": rectToDictionary(rightRect),
            "bottomRect": rectToDictionary(bottomRect)
        ]
    }
    
    func convertPtRectToPx(_ ptRect: CGRect) -> CGRect {
        let scale = UIScreen.main.scale
        return CGRect(
            x: ptRect.origin.x * scale,
            y: ptRect.origin.y * scale,
            width: ptRect.size.width * scale,
            height: ptRect.size.height * scale
        )
    }
    
    func buildAvoidAreaResultAuto(rect: CGRect, areaType: SafeAreaType) -> [String: Any] {
        guard currentKeyWindow() != nil else {
            return [:]
        }
        
        var topRect = CGRect.zero
        var bottomRect = CGRect.zero
        var leftRect = CGRect.zero
        var rightRect = CGRect.zero
        var visible = false
        
        let orientation = UIDevice.current.orientation
        let orientationValue: Int
        switch orientation {
        case .portrait:
            orientationValue = 0
        case .landscapeLeft:
            orientationValue = 1
        case .landscapeRight:
            orientationValue = 2
        case .portraitUpsideDown:
            orientationValue = 3
        default:
            orientationValue = -1
        }
        
        let rectInPx = convertPtRectToPx(rect)
        
        if areaType == .statusBar {
            topRect = rectInPx
            visible = true
        } else if areaType == .navigationToolbar {
            bottomRect = rectInPx
            visible = true
        } else {
            switch orientationValue {
            case 0:
                topRect = rectInPx
                visible = true
            case 1:
                leftRect = rectInPx
                visible = true
            case 2:
                rightRect = rectInPx
                visible = true
            case 3:
                bottomRect = rectInPx
                visible = true
            default:
                break
            }
        }
        
        return buildAvoidAreaResult(
            visible: visible,
            leftRect: leftRect,
            topRect: topRect,
            rightRect: rightRect,
            bottomRect: bottomRect
        )
    }
    
    private func rectToDictionary(_ rect: CGRect) -> [String: CGFloat] {
        return [
            "left": rect.origin.x,
            "top": rect.origin.y,
            "width": rect.size.width,
            "height": rect.size.height
        ]
    }
}


